using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using MongoDB.Driver;
using Sustainalytics.Entities;
using Sustainalytics.Synchronization.Interface;
using Sustainalytics.Synchronization.Validation.Internals;
using Sustainalytics.Synchronization.Validation.ValidationRules.Common;

namespace Sustainalytics.Synchronization.Validation.Restore
{
    public class EntityProfileRestoreProfileRestoreHandler<T> : AbstractProfileRestoreHandler<T>
        where T : IEntity
    {
        public ILogger _logger;

        public EntityProfileRestoreProfileRestoreHandler(ValidationMongoCollection<T> collections, ILogger logger)
            : base(collections)
        {
            _logger = logger;
        }

        public override async Task ForEachElementsAsync(InvalidCompaniesList invalidCompanies)
        {
            if (invalidCompanies == null)
            {
                throw new ArgumentNullException("invalidCompanies");
            }

            var companyIds = invalidCompanies.GetCompanies(ResearchProduct);

            if (!companyIds.Any())
            {
                return;
            }

            _logger.Log($"Performing restore profile for {ResearchProduct} companies.");
            _logger.Log($"{companyIds.Count} companies will be restored.");

            if (ValidatedDataCollection == null)
                throw new ArgumentNullException("ValidatedDataCollection", "validated data collection is null.");

            await ValidatedDataCollection.DeleteManyAsync(GetEntityFilter(companyIds)).ConfigureAwait(false);

            var oldEntities = await GetReferenceElementsAsync(companyIds).ConfigureAwait(false);

            await GetInvalidNewEntitiesAsync(oldEntities, companyIds, x => x.Id).ConfigureAwait(false);
            
            if (!oldEntities.Any())
            {
                return;
            }

            await ValidatedDataCollection.InsertManyAsync(oldEntities).ConfigureAwait(false);
        }


        protected override FilterDefinition<T> GetEntityFilter(IEnumerable<Guid> companyIds)
        {
           return Builders<T>.Filter.In(x => x.Id, companyIds);
        }
    }

    public class ComprehensiveEntityProfileRestoreProfileRestoreHandler<T> : EntityProfileRestoreProfileRestoreHandler<T>
        where T : IEntity
    {
        public ComprehensiveEntityProfileRestoreProfileRestoreHandler(ValidationMongoCollection<T> collections, ILogger logger)
            : base(collections, logger)
        {
            ResearchProduct = BusinessResearchProduct.Comprehensive;
        }
    }

    public class CoreEntityProfileRestoreProfileRestoreHandler<T> : EntityProfileRestoreProfileRestoreHandler<T>
        where T : IEntity
    {
        public CoreEntityProfileRestoreProfileRestoreHandler(ValidationMongoCollection<T> collections, ILogger logger)
            : base(collections, logger)
        {
            ResearchProduct = BusinessResearchProduct.Core;
        }
    }

    public class GovernanceEntityProfileRestoreProfileRestoreHandler<T> : EntityProfileRestoreProfileRestoreHandler<T>
        where T : IEntity
    {
        public GovernanceEntityProfileRestoreProfileRestoreHandler(ValidationMongoCollection<T> collections, ILogger logger)
            : base(collections, logger)
        {
            ResearchProduct = BusinessResearchProduct.CorporateGovernance;
        }
    }

    public class EventsEntityProfileRestoreProfileRestoreHandler<T> : EntityProfileRestoreProfileRestoreHandler<T>
       where T : IEntity
    {
        public EventsEntityProfileRestoreProfileRestoreHandler(ValidationMongoCollection<T> collections, ILogger logger)
            : base(collections, logger)
        {
            ResearchProduct = BusinessResearchProduct.Events;
        }
    }
}